package edu.nf.placeorder.service;

import edu.nf.placeorder.common.config.RabbitConfig;
import edu.nf.placeorder.common.exception.OrderException;
import edu.nf.placeorder.entity.Goods;
import edu.nf.placeorder.entity.Order;
import edu.nf.placeorder.mapper.GoodsMapper;
import edu.nf.placeorder.mapper.OrderMapper;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.boot.autoconfigure.context.MessageSourceProperties;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.List;
import java.util.UUID;

/**
 * @author wangl
 * @date 2025/9/28
 * 订单服务
 */
@Service
@RequiredArgsConstructor
public class OrderService {

    private final OrderMapper orderMapper;

    private final GoodsMapper goodsMapper;

    private final RedisTemplate<String, Integer> redisTemplate;

    private final RabbitTemplate rabbitTemplate;

    /**
     * 注入缓存管理器，用于获取相应的缓存对象
     */
    private final CacheManager cacheManager;
    private final MessageSourceProperties messageSourceProperties;

    /**
     * 在执行Bean的init方法时进行缓存预热
     */
    @PostConstruct
    private void initStock() {
        //1. 商品信息和库存分开存储
        // {product:1001   json}
        // {product:stock:1001:   1000}
        //2. 使用Hash
        // product:1001 name iphone price 6999 stock 100
        List<Goods> goodsList = goodsMapper.listGoods();
        goodsList.forEach(goods -> {
            redisTemplate.opsForValue().set(GoodsEnum.STOCK_PREFIX.value() + goods.getGid(), goods.getStock());
        });
    }

    /**
     * 下单
     * @param gid
     */
    public void placeOrder(int gid) {
        /*if(goodsMapper.decrStock(gid) == 0) {
            throw new OrderException(1000, "库存不足");
        }*/
        //预扣库存
        decrStock(gid);
        //生成订单
        int orderId = createOrder(gid);
        //发送延消息
        sendDelayMessage(orderId);
    }

    /**
     * 扣减缓存中的库存
     * @param gid
     */
    /*private void decrStock(int gid) {
        //执行库存扣减斌返回剩余库存量
        Long stock = redisTemplate.opsForValue().decrement(GoodsEnum.KEY_PREFIX.value() + gid);
        if(stock != null && stock < 0) {
            //防止出现-1的库存
            redisTemplate.opsForValue().increment(GoodsEnum.KEY_PREFIX.value() + gid);
            throw new OrderException(1000, "库存不足");
        }
    }*/

    /**
     * 使用lua脚本实现库存的扣减
     * @param gid
     */
    private void decrStock(int gid) {
        //先断本地缓存中是否标记过此售馨商品，如果标记了则表示已经卖完
        Cache localCache = cacheManager.getCache("localCache");
        if(localCache.get(GoodsEnum.STOCK_PREFIX.value() + gid) != null) {
            throw new OrderException(1000, "商品已售馨");
        }
        //创建lua脚本需要的keys集合
        //由于redis引擎接收key是一个集合，
        //因此只有一个key也要封装为集合对象
        List<String> keys = Collections.singletonList(GoodsEnum.STOCK_PREFIX.value() + gid);
        //创建RedisScript(redis脚本执行器)并设置lua脚本的文件位置
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("lua/stock.lua")));
        //设置lua执行后返回的数据类型
        redisScript.setResultType(Long.class);
        //执行lua脚本并传入keys集合,执行后返回库存量
        Long stock = redisTemplate.execute(redisScript, keys);
        if (stock < 0) {
            //标记本地缓存
            localCache.put(GoodsEnum.STOCK_PREFIX.value() + gid, true);
            throw new OrderException(1000, "商品已售馨");
        }

    }

    /**
     * 创建订单
     * @param gid
     */
    private int createOrder(int gid) {
        try {
            //创建订单
            Order order = new Order();
            //0:未支付  1:已支付  2:无效订单
            order.setStatus(0);
            order.setUserId(1);
            order.setGid(gid);
            orderMapper.addOrder(order);
            return order.getOrderId();
        } catch (Exception e) {
            //下单失败回填缓存
            redisTemplate.opsForValue().increment(GoodsEnum.STOCK_PREFIX.value() + gid);
            throw new OrderException(10001, "下单失败");
        }
    }

    /**
     * 延迟投递消息
     * @param orderId
     */
    private void sendDelayMessage(int orderId) {
        CorrelationData cdata = new CorrelationData(UUID.randomUUID().toString());
        //延迟发送
        rabbitTemplate.convertAndSend(RabbitConfig.DELAY_EXCHANGE,
                RabbitConfig.DELAY_KEY, orderId, messageSourceProcessor -> {
                    //设置延迟时间
                    messageSourceProcessor.getMessageProperties().setDelayLong(RabbitConfig.DELAY_TIME);
                    //返回这个消息处理器
                    return messageSourceProcessor;
                }, cdata);
    }
}